package com.intelligent.ispc.core.service.impl;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.intelligent.ispc.common.mapper.BeanMapper;
import com.intelligent.ispc.common.persistence.Hibernates;
import com.intelligent.ispc.common.utils.ApplicationException;
import com.intelligent.ispc.common.utils.ExceptionMessages;
import com.intelligent.ispc.core.dto.ResourceDto;
import com.intelligent.ispc.core.dto.RoleDto;
import com.intelligent.ispc.core.entity.Resource;
import com.intelligent.ispc.core.entity.Role;
import com.intelligent.ispc.core.repository.jpa.PermissionDao;
import com.intelligent.ispc.core.repository.jpa.RoleDao;
import com.intelligent.ispc.core.service.PermissionService;
import com.intelligent.ispc.utils.Constant;
import com.intelligent.ispc.utils.ResourceCategory;
import com.intelligent.ispc.utils.SystemConfig;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;

import javax.persistence.criteria.*;
import java.util.*;

/**
 * Created by Rovey.Luo on 2015/11/25.
 */
@Service
public class PermissonServiceImpl implements PermissionService{
    private static Logger logger = LoggerFactory.getLogger(PermissonServiceImpl.class);

    @Autowired
    private PermissionDao permissionDao;

    @Autowired
    private RoleDao roleDao;


    @Override
    public void savePermission(ResourceDto resourceDto) {
        if(resourceDto == null || resourceDto.getId() == null){
            throw new ApplicationException(ExceptionMessages.EXCEPTION_PARAMETER_INPUT_INVALID);
        }

        Resource resourceDtoId = permissionDao.findById(resourceDto.getId());

        if (resourceDtoId != null) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_OBJECT_ALREADY_EXISTS);
        }

        Resource resource = new Resource();
        BeanMapper.copy(resourceDto,resource);
        logger.debug("Saving resource:{}",resourceDto);
        permissionDao.save(resource);
        logger.debug("Saving done, resource={}",resource.getName());
    }

    @Override
    public void updatePermission(ResourceDto resourceDto) {
        if (resourceDto == null || resourceDto.getId() == null) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_PARAMETER_INPUT_INVALID);
        }
        Resource resource = new Resource();
        BeanMapper.copy(resourceDto, resource);

        logger.debug("Updating user:{}", resourceDto);
        permissionDao.save(resource);
        logger.info("Update done, user={}", resource.getName());
    }

    @Override
    public void deletePermission(String uuid) {
        if (uuid == null) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_PARAMETER_INPUT_INVALID);
        }
        Resource resource = permissionDao.findById(Long.valueOf(uuid));
        if (resource == null) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_OBJECT_DOES_NOT_EXIST);
        }
        logger.debug("Deleting resource:{}", resource.getName());
        permissionDao.delete(resource);
        logger.info("Delete done, user={}", resource.getName());

    }

    @Override
    public ResourceDto findByUuid(String uuid) {
        if (uuid == null) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_PARAMETER_INPUT_INVALID);
        }

        Resource resource = permissionDao.findById(Long.valueOf(uuid));

        if (resource == null) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_OBJECT_DOES_NOT_EXIST);
        }
        ResourceDto resourceDto = new ResourceDto();
        BeanMapper.copy(resource,resourceDto);

        return resourceDto;
    }

    @Override
    public ResourceDto findByLoginName(String name) {
        if (StringUtils.isBlank(name)) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_PARAMETER_INPUT_INVALID);
        }

        Resource resource = permissionDao.findByName(name);

        if (resource == null) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_OBJECT_DOES_NOT_EXIST);
        }

        ResourceDto resourceDto = new ResourceDto();
        BeanMapper.copy(resource,resourceDto);

        return resourceDto;
    }

    @Override
    public List<ResourceDto> findAllResources() {
        List<Resource> resources = (List<Resource>)permissionDao.findAll();
        if (resources == null) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_OBJECT_DOES_NOT_EXIST);
        }
        List<ResourceDto> resourceDtos = Lists.newArrayList();

        for(Resource resource : resources){
            ResourceDto resourceDto = new ResourceDto();
            BeanMapper.copy(resource,resourceDto);

            resourceDtos.add(resourceDto);
        }

        return resourceDtos;
    }

    @Override
    public Map<String, List<ResourceDto>> findAllResourcesWithCategory() {
        List<Resource> resources = (List<Resource>)permissionDao.findAll();
        if (resources == null) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_OBJECT_DOES_NOT_EXIST);
        }
        LinkedHashMap<String, List<ResourceDto>> dtoMap = new LinkedHashMap<String, List<ResourceDto>>();
        for(Resource resource : resources){
            String code = resource.getCode();
            String[] codes  = resource.getCode().split(":");
            String codeKey = code.substring(0, code.lastIndexOf(":"));
            if (!dtoMap.containsKey(codeKey)) {
                dtoMap.put(codeKey, new ArrayList<ResourceDto>());
            }

            ResourceDto resourceDto = new ResourceDto();
            BeanMapper.copy(resource,resourceDto);
            dtoMap.get(codeKey).add(resourceDto);
        }

        List<ResourceDto> resourceDtoList = Lists.newArrayList();
        Set<String> keys = dtoMap.keySet();
        for (String key : keys) {
            List<ResourceDto> resourceDtos = dtoMap.get(key);
            resourceDtoList.addAll(resourceDtos);
        }

        Map<String, List<ResourceDto>> map = Maps.newHashMap();
        for(ResourceDto resourceDto : resourceDtoList) {
            if(map.containsKey(resourceDto.getCategory().toString())) {
                List<ResourceDto> tlist = map.get(resourceDto.getCategory().toString());
                tlist.add(resourceDto);
            } else {
                List<ResourceDto> tlist = Lists.newArrayList();
                tlist.add(resourceDto);
                map.put(resourceDto.getCategory().toString(), tlist);

            }
        }

        return map;
    }

    @Override
    public List<RoleDto> resourceCanAddRole(String uuid) {
        if (uuid == null) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_PARAMETER_INPUT_INVALID);
        }
        Resource resource = permissionDao.findById(Long.valueOf(uuid));
        if (resource == null) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_OBJECT_DOES_NOT_EXIST);
        }
        List<Role> roleList = (List<Role>) roleDao.findAll();

        for (Role role : resource.getRoles()) {
            String roleId = role.getId().toString();
            for(Role roleLists : roleList){
               String roleListId = roleLists.getId().toString();
                if(roleId.equals(roleListId)){
                    roleList.remove(roleLists);
                    break;
                }
            }
        }
        List<RoleDto> roleDtos = Lists.newArrayList();
        for (Role role : roleList) {
            RoleDto roleDto = new RoleDto();
            Hibernates.initLazyProperty(role.getResources());
            BeanMapper.copy(role, roleDto);
            if (Constant.ROLE_DISABLE.equals(roleDto.getEnabled()))
                continue;
            roleDtos.add(roleDto);
        }
        return roleDtos;
    }

    @Override
    public void assignRolesForPermission(String resourceId, List<String> roleIds) {
        logger.debug("Assign roles for resource:resourceId={}", resourceId);
        if (resourceId == null) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_PARAMETER_INPUT_INVALID);
        }
        Resource resource = permissionDao.findById(Long.valueOf(resourceId));

        if (resource == null) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_OBJECT_DOES_NOT_EXIST);
        }

        String resourceCode = resource.getCode();

        String[] resourceCodes = resourceCode.split(":");
        List<Resource> resouceList = Lists.newArrayList();
        List<Resource> firstResouceList = Lists.newArrayList();
        List<Resource> secondResouceList = Lists.newArrayList();

        if (ArrayUtils.isNotEmpty(resourceCodes)) {
            firstResouceList = permissionDao.findByCodeLike(resourceCodes[0]+"%");

            if (resourceCode.contains("*") && resourceCodes.length == 2) {
                resouceList = firstResouceList;
            } else if (resourceCodes.length == 3) {
                List<Resource> resouceTempList = permissionDao.findByCodeLike(resourceCodes[0] + ":" + resourceCodes[1]+"%");
                if (resourceCode.contains("*")) {
                    resouceList = resouceTempList;
                } else {
                    secondResouceList = resouceTempList;
                }
            }
        }

        Role role = null;
        for (int i = 0; i < roleIds.size(); i++) {
            String roleId = roleIds.get(i);
            if (roleId == null) {
                throw new ApplicationException(ExceptionMessages.EXCEPTION_PARAMETER_INPUT_INVALID);
            }
            logger.debug("Assign roles for resource:roleId={}", roleId);
            role = roleDao.findById(Long.valueOf(roleId));
            if (role == null) {
                throw new ApplicationException(ExceptionMessages.EXCEPTION_OBJECT_DOES_NOT_EXIST);
            }

            for (Resource resource1 : resouceList) {
                boolean hasResource = false;
                for (Resource resource2 : role.getResources()) {
                    if (resource1.getCode().equals(resource2.getCode())) {
                        hasResource = true;
                        break;
                    }
                }
                if (!hasResource) {
                    resource1.getRoles().add(role);
                    role.getResources().add(resource1);
                }
            }

            if (resouceList.isEmpty()) {
                resource.getRoles().add(role);
                role.getResources().add(resource);
            }

            int totalSecondResources = 0;
            int totalFirstResources = 0;
            boolean isFirstResource = false;
            for (Resource resource1 : role.getResources()) {
                if (resourceCodes.length == 3 && resource1.getCode().contains(resourceCodes[0] + ":" + resourceCodes[1])) {
                    if (!resource1.getCode().contains("*")) {
                        totalSecondResources++;
                    } else
                        totalSecondResources--;

                }
                if (resource1.getCode().contains(resourceCodes[0])) {
                    totalFirstResources++;
                    if (resource1.getCode().split(":").length== 2 && resource1.getCode().contains("*")) {
                        isFirstResource = true;
                    }
                }
            }

            if (resourceCodes.length == 3 && totalSecondResources == secondResouceList.size()-1) {
                Resource resource1 = permissionDao.findByCode(resourceCodes[0] + ":" + resourceCodes[1] + ":*");
                role.getResources().add(resource1);
                totalFirstResources++;
            }

            if (totalFirstResources == firstResouceList.size()-1) {
                if (!isFirstResource) {
                    Resource resource1 = permissionDao.findByCode(resourceCodes[0] + ":*");
                    role.getResources().add(resource1);
                }
            }
            logger.info("Assign roles for resource,roleName={}", role.getName());
            roleDao.save(role);

        }

    }

    @Override
    public List<RoleDto> searchRoles(List<String> roleIds) {
        List<Role> roles = Lists.newArrayList();
        for (int i = 0; i < roleIds.size(); i++) {
            String roleId = roleIds.get(i);
            Role role = roleDao.findById(Long.valueOf(roleId));
            roles.add(role);
        }
        List<RoleDto> roleDtos = Lists.newArrayList();
        for (Role role : roles) {
            RoleDto roleDto = new RoleDto();
            BeanMapper.copy(role, roleDto);
            roleDtos.add(roleDto);
        }
        return roleDtos;
    }

    @Override
    public void deleteRoleForPermission(String permissionId, String roleId) {
        logger.debug("Delete role for resource:permissionId={}", permissionId);
        if(permissionId == null){
            throw new ApplicationException(ExceptionMessages.EXCEPTION_PARAMETER_INPUT_INVALID);
        }
        Resource resource = permissionDao.findById(Long.valueOf(permissionId));
        if (resource == null) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_OBJECT_DOES_NOT_EXIST);
        }
        Role role = roleDao.findById(Long.valueOf(roleId));
        logger.debug("Delete role for resource:roleId={}", roleId);
        if (role == null) {
            throw new ApplicationException(ExceptionMessages.EXCEPTION_OBJECT_DOES_NOT_EXIST);
        }

        String resourceCode = resource.getCode();
        String[] resourceCodes = resourceCode.split(":");

        List<Resource> resouceList = Lists.newArrayList();
        if (ArrayUtils.isNotEmpty(resourceCodes)) {
            if (resourceCode.contains("*") && resourceCodes.length == 2) {
                resouceList = permissionDao.findByCodeLike(resourceCodes[0]+"%");;
            } else if (resourceCodes.length == 3) {
                if (resourceCode.contains("*")) {
                    resouceList = permissionDao.findByCodeLike(resourceCodes[0] + ":" + resourceCodes[1]+"%");
                }
            }
        }

        for (Resource resource1 : resouceList) {
            boolean hasResource = false;
            for (Resource resource2 : role.getResources()) {
                if (resource1.getCode().equals(resource2.getCode())) {
                    hasResource = true;
                    break;
                }
            }
            if (hasResource) {
                role.getResources().remove(resource1);
            }
        }

        if (resouceList.isEmpty()) {
            role.getResources().remove(resource);
        }

        for (Resource resource1 : role.getResources()) {
            if (resource1.getCode().contains("*")) {
                Resource secondResource = permissionDao.findByCode(resourceCodes[0] + ":" + resourceCodes[1] + ":*");
                role.getResources().remove(secondResource);

                Resource firstResource = permissionDao.findByCode(resourceCodes[0] + ":*");
                role.getResources().remove(firstResource);
                break;
            }
        }
        roleDao.save(role);
        logger.info("Delete role for resource,roleName={}", role.getName());
    }
}
